/***************************************************************
*
* DS_STRING.CPP
*
* string
***************************************************************/

#include "stdafx.h"
//#include <ap/settings/ds_settings.h>
//
//#include <stdarg.h>
//
//#include "ap\ap_comm.h"
//#include "ds_string.h"
//#include "ds_algorithm.h"

#if defined _AP_PS3
   dsTSTRING<char> emptyStr __attribute__((init_priority(101))) (0);
   dsTSTRING<wchar_t> emptyWStr __attribute__((init_priority(101))) (0);
#endif

#if defined (__PLUGIN__) && defined (NET_VIRTUAL_TEST)
   static __declspec(thread) dsSTRING *_dsBufStr = 0;
#else
   static AP_DECLARE_IN_TLS dsSTRING *_dsBufStr = 0;
#endif


/*************************************************************
* GetDsBufStr()
*
*************************************************************/
dsSTRING &GetDsBufStr()
{
   if (!_dsBufStr) {
      _dsBufStr   = apNEW dsSTRING;
   }
   
   return *_dsBufStr;
}

/*************************************************************
* dsSPrintf()
*
*************************************************************/
dsSTRING __CDECL dsSPrintf(const char *fmt, ...)
{
   va_list a;
   va_start(a, fmt);
   dsSPrintfVS(GetDsBufStr(), fmt, a);
   va_end(a);
   return GetDsBufStr();
}

/*************************************************************
* dsSPintf()
*
*************************************************************/
dsSTRING __CDECL dsSPrintfV(const char *fmt, va_list a)
{
   dsSPrintfVS(GetDsBufStr(), fmt, a);
   return GetDsBufStr();
}

/*************************************************************
* dsSPintf()
*
*************************************************************/
void __CDECL dsSPrintfS(dsSTRING &str, const char *fmt, ...)
{
   va_list a;
   va_start(a, fmt);
   dsSPrintfVS(str, fmt, a);
   va_end(a);
   return;
}

/*************************************************************
* dsSPintf()
*
*************************************************************/
void __CDECL dsSPrintfVS(dsSTRING &str, const char *fmt, va_list a)
{
   for (int len = 256; len < 0x100000; len *= 2) {
#if  defined(_AP_PS3)
      // @E13: vsnprintf never returns -1
      int res = vsnprintf(str.Lock(len - 1), len, fmt, a);
      if (res != -1 && res < len - 1)  {
         str.Unlock();
         break;
      } else {
         str.Unlock();
      }
#else
      if (_vsnprintf(str.Lock(len - 1), len, fmt, a) != -1)  {
         str.Unlock();
         break;
      } else {
         str.Unlock();
      }
#endif      
   }
   return;
}

/*************************************************************
* dsStrToFcc()
*
*************************************************************/
DWORD dsStrToFcc(const char *str)
{
   DWORD fcc = 0;
   for (int i = 0; i < 4 && str[i]; i++) {
      fcc = (fcc << 8) + str[i];
   }
   return fcc;
}

/*************************************************************
* dsStrToFcc()
*
*************************************************************/
dsSTRING dsFccToStr(DWORD fcc)
{
   GetDsBufStr().Reserve(4);
   GetDsBufStr().Clear();
   for (int i = 3; i >=0; i--) {
      char c = (char)((fcc >> (i*8)) & 0xFF);
      if (c) {
         GetDsBufStr() += c;
      } else {
         break;
      }
   }
   return GetDsBufStr();
}

static int DigitChar(const char* c)
{
   if ( *c >= '0' && *c <= '9' ) {
      return *c - '0';
   }
   return -1;
}

ARG_ESCAPE_DATA dsStrFindArgEscapes(const dsSTRING &s)
{
   ARG_ESCAPE_DATA d;
   d.min_escape = INT_MAX;
   d.occurrences = 0;
   d.escape_len = 0;

   if ( s.IsEmpty() ) {
      return d;
   }

   const char *begin = s.CStr();
   const char *end = begin + s.Length();

   const char *c = begin;
   while (c != end) {
      while (c != end && *c != '%') {
         ++c;
      }

      if (c == end) {
         break;
      }

      const char *escape_start = c;
      if ( ++c == end ) {
         break;
      }

      if ( DigitChar(c) == -1 ) {
         continue;
      }

      int escape = DigitChar(c);
      ++c;

      if (c != end && DigitChar(c) != -1) {
         escape = (10 * escape) + DigitChar(c);
         ++c;
      }

      if (escape > d.min_escape) {
         continue;
      }

      if (escape < d.min_escape) {
         d.min_escape = escape;
         d.occurrences = 0;
         d.escape_len = 0;
      }

      ++d.occurrences;
      d.escape_len += c - escape_start;
   }

   return d;
}

dsSTRING dsStrReplaceArgEscapes(const dsSTRING &s, const ARG_ESCAPE_DATA &d, int field_width, const dsSTRING &arg, char fillChar)
{
   if ( s.IsEmpty() ) {
      return s;
   }

   const char *begin = s.CStr();
   const char *end = begin + s.Length();

   int abs_field_width = abs(field_width);
   int result_len = s.Length() - d.escape_len + d.occurrences * max(abs_field_width, arg.Length());

   dsSTRING result;
   char *result_buff = result.Lock(result_len);

   char *rc = result_buff;
   const char *c = begin;
   int repl_cnt = 0;
   while (c != end) {
      const char *text_start = c;

      while (*c != '%') {
         ++c;
      }

      const char *escape_start = c++;

      int escape = DigitChar(c);
      if ( escape != -1 ) {
         if (c + 1 != end && DigitChar(c + 1) != -1) {
            escape = (10 * escape) + DigitChar(c + 1);
            ++c;
         }
      }

      if ( escape != d.min_escape ) {
         memcpy(rc, text_start, (c - text_start)*sizeof(char));
         rc += c - text_start;
      } else {
         ++c;

         memcpy(rc, text_start, (escape_start - text_start)*sizeof(char));
         rc += escape_start - text_start;

         UINT pad_chars = max(abs_field_width, arg.Length()) - arg.Length();

         if ( field_width > 0 ) { // left padded
            for (UINT i = 0; i < pad_chars; ++i)
               *(rc++) = fillChar;
         }

         memcpy(rc, arg.CStr(), arg.Length() * sizeof(char));
         rc += arg.Length();

         if ( field_width < 0 ) { // right padded
            for (int i = 0; i < pad_chars; ++i)
               *(rc++) = fillChar;
         }

         if (++repl_cnt == d.occurrences) {
            memcpy(rc, c, (end - c)*sizeof(char));
            rc += end - c;
            STRONG_ASSERT(rc - result_buff == result_len);
            c = end;
         }
      }
   }
   STRONG_ASSERT(rc == result_buff + result_len);

   result.Unlock();
   return result;
}

namespace ds 
{

/*************************************************************
* MakeBytesReadable()
*
* Make readable string for byte values. 
* Example 123456 -> "120.7 Kb"
*************************************************************/
dsSTRING __CDECL MakeBytesReadable(UINT value)
{
   float valuef = float(value);
   if (valuef < 1024.f) {
      return dsSPrintf("%.0f bytes", valuef);
   } else if (value < 1048674.f) {
      return dsSPrintf("%.1f Kb", valuef / 1024.f);
   } else {
      return dsSPrintf("%.1f Mb", valuef / 1048674.f);
   }
}

/*************************************************************
* MakeBytesSpeedReadable()
*
* Make readable string for data transfer rates. 
* Example 123456 -> "120.7 Kbps"
*************************************************************/
dsSTRING __CDECL MakeRateReadable(float speedBytesPerSec)
{
   float speedBits = speedBytesPerSec * 8;
   if (speedBits < 1000.f) {
      return dsSPrintf("%.0f bps", speedBits);
   } else if (speedBits < 1000000.f) {
      return dsSPrintf("%.1f kbps", speedBits / 1000.f);
   } else {
      return dsSPrintf("%.1f Mbps", speedBits / 1000000.f);
   }
}

/*************************************************************
* MakeAlignedStrL()
* 
*************************************************************/
dsSTRING __CDECL MakeAlignedStrL(const char *str, UINT fieldLen, bool cut /*= false*/)
{
   int len = strlen(str);
   int spaces = fieldLen - len;
   if (spaces <= 0) {
      if (!cut || len <= 2) {
         return str;
      } else {
         dsSTRING cut(str);
         cut = cut.SubStr(0, fieldLen);
         cut.At(fieldLen - 1) = '.';
         cut.At(fieldLen - 2) = '.';
         return cut;
      }
   } else {
      dsSTRING strspaces(spaces);
      for (int i = 0; i < spaces; ++i)
         strspaces += ' ';
      return str + strspaces;
   }
}

/*************************************************************
* MakeAlignedStrR()
* 
*************************************************************/
dsSTRING __CDECL MakeAlignedStrR(const char *str, UINT fieldLen, bool cut /*= false*/)
{
   int len = strlen(str);
   int spaces = fieldLen - len;
   if (spaces <= 0) {
      if (!cut || len <= 2) {
         return str;
      } else {
         dsSTRING cut(str);
         cut = cut.SubStr(0, fieldLen);
         cut.At(fieldLen - 1) = '.';
         cut.At(fieldLen - 2) = '.';
         return cut;
      }
   } else {
      dsSTRING strspaces(spaces);
      for (int i = 0; i < spaces; ++i)
         strspaces += ' ';
      return strspaces + str;
   }
}

}
//
// End-of-file DS_STRING.CPP
//
